package org.hackystat.devcathlon.event.impl;

import static org.junit.Assert.assertEquals;

import javax.xml.datatype.XMLGregorianCalendar;

import org.hackystat.dailyprojectdata.client.DailyProjectDataClient;
import org.hackystat.dailyprojectdata.resource.devtime.jaxb.DevTimeDailyProjectData;
import org.hackystat.devcathlon.configuration.ConfigurationManager;
import org.hackystat.devcathlon.event.Event;
import org.hackystat.devcathlon.event.EventScore;
import org.hackystat.devcathlon.match.Match;
import org.hackystat.devcathlon.team.Team;
import org.hackystat.devcathlon.test.DevcathlonTestHelper;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.utilities.tstamp.Tstamp;
import org.junit.Test;
import org.junit.BeforeClass;


/**
 * Tests the Keep The Coverage High event.
 * @author Philip Johnson
 */
public class TestKeepTheCoverageHigh extends DevcathlonTestHelper {
  
  /** The upper threshold of coverage used for these tests. */
  private static int upperThreshold = 90;
  /** The bonus weight for the first time over the threshold for these tests. */
  private static int bonusWeight = 20;
  
  private String teamName = "TestTeam";
  private String teamOwner = "TeamOwner";
  private String matchName = "TestMatch";
  private String member1 = "Member1";
  private String member2 = "Member2";


  
  /**
   * Sets up the DevcathlonConfiguration for use in these test cases. 
   * @throws Exception If problems occur setting up the configuration. 
   */
  @BeforeClass
  public static void setupConfiguration() throws Exception {
    // Set up the testing DevcathlonConfiguration for the event KeepTheCoverageHigh.
    String eventName = KeepTheCoverageHigh.eventName;
    ConfigurationManager configManager = ConfigurationManager.getInstance();
    configManager.setBaseScoreWeight(eventName, 1);
    configManager.getConfiguration(eventName);
    configManager.setWakeupInterval(eventName, 60);
    configManager.addProperty(eventName, KeepTheCoverageHigh.upperThresholdKey, 
        String.valueOf(upperThreshold));
    configManager.addProperty(eventName, KeepTheCoverageHigh.typeOfCoverageKey, "Line");
    configManager.addProperty(eventName, KeepTheCoverageHigh.firstTimeKey, 
        String.valueOf(bonusWeight));
  }
  
  /**
   * Tests this event when there is no sensor data for this team.
   * @throws Exception If there is a bug.
   */
  @Test
  public void testNoData() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);
    
    // Test the event with no sensor data.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("Check base 1", 0, score.getBaseScore());
    assertEquals("Check bonus 1", 0, score.getBonusScore(KeepTheCoverageHigh.firstTimeBonusName));
    assertEquals("Check points 1", 0, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event when there is a single build but no coverage data. 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testSingleBuildNoCoverage() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Send a single build data, but no coverage data. 
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    
    // Now compute the event.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("Check base 2", 0, score.getBaseScore());
    assertEquals("Check bonus 2", 0, score.getBonusScore(KeepTheCoverageHigh.firstTimeBonusName));
    assertEquals("Check points 2", 0, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event when there is a single build followed by a single instance of low coverage.
   * @throws Exception If there is a bug.
   */
  @Test
  public void testSingleBuildCoverageSequence() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Send a build followed by some coverage. 
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), 50, 100);
    
    // Now compute the event.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("Check base 3", 0, score.getBaseScore());
    assertEquals("Check bonus 3", 0, score.getBonusScore(KeepTheCoverageHigh.firstTimeBonusName));
    assertEquals("Check points 3", 0, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event when there is a single build followed by a single instance of high coverage
   * (in other words, over threshold).
   * @throws Exception If there is a bug.
   */
  @Test
  public void testSingleBuildHighCoverageSequence() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Send a build followed by high coverage. 
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), upperThreshold + 1, 100);
    
    // Now compute the event.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("Check base 4", 0, score.getBaseScore());
    assertEquals("Check bonus 4", 1, score.getBonusScore(KeepTheCoverageHigh.firstTimeBonusName));
    assertEquals("Check points 4", 20, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event when there is a single build followed by a single instance of high coverage
   * (in other words, over threshold).
   * @throws Exception If there is a bug.
   */
  @Test
  public void testRisingCoverageSequence() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Send a build followed by coverage = 20%, and compute the score.
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), 20, 100);
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    // Now send another build followed by coverage = 30%, and compute the score. 
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), 30, 100);
    score = event.computeScore(match, team, Tstamp.makeTimestamp());
    // Now check the score.
    //System.out.println(score.getExplanation());
    assertEquals("Check base 5", 10, score.getBaseScore());
    assertEquals("Check bonus 5", 0, score.getBonusScore(KeepTheCoverageHigh.firstTimeBonusName));
    assertEquals("Check points 5", 10, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event when coverate goes from 85 to 95.
   * @throws Exception If there is a bug.
   */
  @Test
  public void testRisingCoverageOverThresholdSequence() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Send a build followed by coverage = 85%, and compute the score.
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), 85, 100);
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    // Now send another build followed by coverage = 95%, and compute the score. 
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), 95, 100);
    score = event.computeScore(match, team, Tstamp.makeTimestamp());
    // Now check the score.
    System.out.println(score.getExplanation());
    assertEquals("Check base 6", 0, score.getBaseScore());
    assertEquals("Check bonus 6", 1, score.getBonusScore(KeepTheCoverageHigh.firstTimeBonusName));
    assertEquals("Check points 6", 20, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event when coverage goes from 85 to 80.
   * @throws Exception If there is a bug.
   */
  @Test
  public void testDroppingCoverageSequence() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Send a build followed by coverage = 85%, and compute the score.
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), 85, 100);
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    // Now send another build followed by coverage = 80%, and compute the score. 
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCoverageData(team.getOwner(), project, Tstamp.makeTimestamp(), 80, 100);
    score = event.computeScore(match, team, Tstamp.makeTimestamp());
    // Now check the score.
    System.out.println(score.getExplanation());
    assertEquals("Check base 7", -5, score.getBaseScore());
    assertEquals("Check bonus 7", 0, score.getBonusScore(KeepTheCoverageHigh.firstTimeBonusName));
    assertEquals("Check points 7", -5, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests the makeDevTimeData() helper method, since there isn't yet any Event using it. 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testDevTime() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new KeepTheCoverageHigh();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Call makeDevTimeData to make 30 minutes of DevTime (this generates 6 DevEvents). 
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    makeDevTimeData(team.getOwner(), project, startTime, 30);
    DailyProjectDataClient dpdClient = match.getDailyProjectDataClient(team);
    
    // Now get the DPD data and check to make sure that 30 minutes of DevTime is computed. 
    DevTimeDailyProjectData dpdData = dpdClient.getDevTime(team.getOwner().getEmail(), 
        project.getName(), startTime);
    assertEquals("Testing makeDevTime", 30, dpdData.getTotalDevTime().intValue());
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }

}
